home *** CD-ROM | disk | FTP | other *** search
- # Miro - an RSS based video player application
- # Copyright (C) 2005-2007 Participatory Culture Foundation
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, write to the Free Software
- # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
- #
- # "Compiles" Democracy templates to Python code
- #
-
- from util import checkU
-
- ###############################################################################
- #### Functions used in repeating templates ####
- ###############################################################################
-
- # These are functions that take in a file object name that will be
- # written to in the output file, an optional id, prefix text, and an
- # argument
-
- # Simply returns text
- def genRepeatText(varname, tid, prefix, text):
- checkU(text)
- return u'%s%s.write(%s)\n' % (prefix,varname, repr(text))
-
- # Returns translated version of text
- def genRepeatTranslate(varname, tid, prefix, args):
- (text, funcDict) = args
- checkU(text)
- # Convert to ascii, strip leading and trailing whitespace and
- # convert interior whitespace to spaces
- text = u' '.join(str(text).strip().split())
-
- if len(funcDict) == 0:
- return u'%s%s.write(_(%s))\n' % (prefix, varname, repr(text))
- else:
- dictName = generateId()
- out = u'%s%s = {}\n' % (prefix, dictName)
- for name in funcDict:
- temp = generateId()
- out = u'%s%s%s = StringIO()\n' % (out, prefix, temp)
- for (func, fargs) in funcDict[name]:
- val = func(temp, tid, prefix, fargs)
- checkU(val)
- out = u'%s%s' % (out, val)
- out = u'%s%s%s.seek(0)\n' % (out, prefix, temp)
- out = u'%s%s%s[%s] = %s.read()\n' % (out, prefix, dictName, repr(str(name)), temp)
-
- out = u'%s%s%s.write(Template(_(%s)).substitute(%s))\n' % (
- out, prefix, varname, repr(text), dictName)
- return out
- # Returns text if function does not evaluate to true
- def genRepeatTextHide(varname, tid, prefix, args):
- (ifValue, text) = args
- checkU(text)
- out = u'%sif not (%s):\n'%(prefix, ifValue)
- out = u'%s%s %s.write(%s)\n' % (out, prefix, varname, repr(text))
- return out
-
- def genQuoteAttr(varname, tid, prefix, value):
- return u'%s%s.write(quoteattr(urlencode(%s)))\n'%(
- prefix, varname, value)
-
- def genRawAttr(varname, tid, prefix, value):
- return u'%s%s.write(quoteattr(%s))\n'%(prefix, varname, value)
-
- # Adds a tid attribute to a tag and closes it
- def genRepeatTID(varname, tid, prefix, args):
- return u'%s%s.write(quoteattr(tid))\n' % (prefix, varname)
-
- # Evaluates key with data
- def genRepeatEvalEscape(varname, tid, prefix, replace):
- return u'%s%s.write(escape(%s))\n' % (prefix, varname, replace)
-
- # Evaluates key with data
- def genRepeatEval(varname, tid, prefix, replace):
- return u'%s%s.write(%s)\n' % (prefix, varname, replace)
-
- # Returns include iff function does not evaluate to true
- def genRepeatIncludeHide(varname, tid, prefix, args):
- (ifValue, name) = args
- f = open(resource.path('templates/%s'%name),'r')
- text = f.read()
- f.close()
- out = u'%sif not (%s):\n'%(prefix, ifValue)
- out = u'%s%s %s.write(%s)\n' % (out, prefix, varname, repr(text.decode('utf-8')))
- return out
-
- def genHideSection(varname, tid, prefix, args):
- (ifValue, funcList) = args
- out = u'%sif not (%s):\n'%(prefix, ifValue)
- for (func, newargs) in funcList:
- val = func(varname,tid,prefix+' ',newargs)
- checkU(val)
- out = u'%s%s' % (out, val)
- return out
-
- def genQuoteAndFillAttr(varname, tid, prefix, value):
- checkU(value)
- return u'%s%s.write(quoteAndFillAttr(%s,locals()))\n'%(prefix,varname,repr(value))
-
- def genUpdateHideOnView(varname, tid, prefix, args):
- (viewName, ifValue, attrs, nodeId) = args
-
- out = u'%s_hideFunc = lambda : %s\n' % (prefix, ifValue)
- out = u'%s%s_dynHide = _hideFunc()\n' % (out, prefix)
- out = u'%s%sif _dynHide:\n' % (out, prefix)
- out = u'%s%s %s.write(u" style=\\\"display:none\\\">")\n' % (
- out, prefix, varname)
- out = u'%s%selse:\n%s %s.write(u">")\n' % (
- out, prefix, prefix, varname)
-
- out = u'%s%shandle.addUpdateHideOnView(%s,%s,_hideFunc,_dynHide)\n' % (
- out, prefix, repr(nodeId), viewName)
- return out
-
- def genInsertBodyTagExtra(varname, tid, prefix, args):
- return u'%s%s.write(u" " + bodyTagExtra)\n' % (prefix, varname)
-
- def genExecuteTemplate(varname, tid, prefix, args):
- filename, methodArgs = args
- methodCall = u"fillStaticTemplate(%r, onlyBody=True" % filename
- for name, value in methodArgs.items():
- methodCall += u', %s=%s' % (name, value)
- methodCall += u')'
- return u'%s%s.write(%s)\n' % (prefix, varname, methodCall)
-
- from xml import sax
- from StringIO import StringIO # Oh! Why can't cStringIO support unicode?
- from templatehelper import quoteattr, escape, HTMLPattern, attrPattern, resourcePattern, rawAttrPattern, generateId
- import re
- import os
- import stat
-
- #Setup gettext
- #gettext.install('dtv', 'resources/gettext')
-
- # Limitations:
- # - t:hideIf tags are only dynamically updated whenever there is dynamic
- # activity on a t:repeatForView in the document (easily fixed)
- # - Style tags are overwritten for hidden nodes
- # - id tags are inserted in appropriate places
- # - An empty span is always created after a repeatForView, so we can't
- # use it inside of a table
- # - Currently, we're using the translate and name attributes found on
- # this page to mark text for translation
- # http://www.zope.org/DevHome/Wikis/DevSite/Projects/ComponentArchitecture/ZPTInternationalizationSupport
-
- # To Do:
- # - Take a lock around the entire database while template is being filled
- # - Improve error handling (currently some random exception is thrown if
- # the template is poorly structured)
- #
- # Eventually, we may add support for more attributes
-
- class res:
- def path(self, rel_path):
- global resourcePath
- return os.path.join(resourcePath, rel_path)
-
- resource = res()
-
- def setResourcePath(path):
- global resourcePath
- resourcePath = path
-
- ###############################################################################
- #### Public interface ####
- ###############################################################################
-
- #
- # Compile the template given in inFile to python code in outFile
- #
- def compileTemplate(inFile, *args, **kwargs):
- handle = MetaHandle()
- tcc = TemplateContentCompiler(handle, inFile, *args, **kwargs)
- p = sax.make_parser()
- p.setFeature(sax.handler.feature_external_ges, False)
- p.setContentHandler(tcc)
- p.parse(resource.path("templates/%s" % inFile))
- return (tcc, handle)
-
- # Returns a list of templates with URLs relative to the template
- # resource directory
- def findTemplates(tpath):
- templates = []
- folders = []
- for template in os.listdir(tpath):
- if (template.find('.svn') == -1 and
- template.find('_svn') == -1 and
- not (template.startswith('.') or template.startswith('#') or
- template.endswith('~') or template.endswith('.js') or
- template.endswith('.html'))):
- mode = os.stat(os.path.join(tpath,template))[stat.ST_MODE]
- if stat.S_ISDIR(mode):
- folders.append(template)
- else:
- templates.append(template)
- return (folders, templates)
-
- def modifiedTime(dir):
- maxTime = 0
- try:
- for (dirpath, dirnames, filenames) in os.walk(dir):
- for f in filenames:
- if -1 == dirpath.find('.svn') and -1 == dirpath.find('_svn'):
- t = os.stat(os.path.join(dirpath, f)).st_mtime
- if t > maxTime:
- maxTime = t
- return maxTime
- except:
- return 0
-
- def compileAllTemplates(root):
- setResourcePath(os.path.join(root,'resources'))
- source = resource.path('templates')
- sourceTime = modifiedTime(source)
- dest = resource.path(os.path.join('..','portable','compiled_templates'))
- if not os.path.isdir(dest):
- os.makedirs(dest)
- destTime = modifiedTime(dest)
- compilerTime = os.stat(resource.path(os.path.join(
- '..','portable','template_compiler.py'))).st_mtime
- if (sourceTime > destTime) or (compilerTime > destTime):
- compileTemplates()
-
- def compileTemplates(tpath = None):
- outdir = resource.path(os.path.join('..','portable','compiled_templates'))
- if not os.path.isdir(outdir):
- os.makedirs(outdir)
- indir = resource.path('templates')
- if tpath is not None:
- print "Compiling %s" % tpath
- outdir = os.path.join(outdir, tpath)
- try:
- os.makedirs(outdir)
- except:
- pass
- indir = os.path.join(indir, tpath)
-
- manifest = open(os.path.join(outdir,'__init__.py'),'wb')
- manifest.write('# This is a generated file. Do not edit.\n\n')
-
- (folders, templates) = findTemplates(indir)
- for template in templates:
- outFile = os.path.join(outdir,template.replace('-','_')+'.py')
- if tpath is None:
- sourceFile = template
- else:
- sourceFile = os.path.join(tpath, template)
- print "Compiling '%s' template to %s" % (sourceFile, outFile)
- (tcc, handle) = compileTemplate(sourceFile)
- f = open(outFile,"wb")
- f.write(tcc.getOutput().encode('utf-8'))
- f.close()
- manifest.write("import %s\n" % template.replace('/','.').replace('\\','.').replace('-','_'))
- for folder in folders:
- manifest.write("import %s\n" % folder.replace('/','.').replace('\\','.').replace('-','_'))
- compileTemplates(folder)
- manifest.close()
-
- class TemplateError(Exception):
- def __init__(self, message):
- self.message = message
- def __str__(self):
- return self.message
-
- ###############################################################################
- #### Main template filling code ####
- ###############################################################################
-
- ##
- # SAX version of templating code
- class TemplateContentCompiler(sax.handler.ContentHandler):
- def __init__(self, handle, name, debug = False, onlyBody = False,
- addIdToTopLevelElement=False):
- self.handle = handle
- self.debug = debug
- self.onlyBody = onlyBody
- self.addIdToTopLevelElement = addIdToTopLevelElement
- self.name = name
- self.isDynamic = False
-
- def getOperationList(self):
- return self.outputLists[0]
-
- def getOutput(self, data = None):
- fo = StringIO()
- self.render(fo)
- return fo.getvalue()
-
- def render(self, fileobj):
- fileobj.write(u'# This is a generated file. Do not edit.\n')
- fileobj.write(u'from template import Handle, fillAttr, quoteAndFillAttr, fillStaticTemplate\n')
- fileobj.write(u'from StringIO import StringIO\n')
- fileobj.write(u'from xhtmltools import urlencode\n')
- fileobj.write(u'from templatehelper import quoteattr, escape\n')
- fileobj.write(u'from string import Template\n')
- fileobj.write(u'import app\n')
- fileobj.write(u'import views\n')
- fileobj.write(u'import sorts\n')
- fileobj.write(u'import indexes\n')
- fileobj.write(u'import filters\n')
- fileobj.write(u'import resources\n')
- fileobj.write(u'import gtcache\n')
- fileobj.write(u'_ = gtcache.gettext\n')
- fileobj.write(u'def fillTemplate(domHandler, dtvPlatform, eventCookie, bodyTagExtra, *args, **kargs):\n')
- self.handle.render(fileobj)
- fileobj.write(u'\n\n out = StringIO()\n')
-
- if not self.onlyBody:
- fileobj.write(u' out.write(u"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\\n<!DOCTYPE html PUBLIC \\\"-//W3C//DTD XHTML 1.0 Strict//EN\\\" \\\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\\\">\\n")\n')
-
- for count in range(len(self.outputLists[0])):
- (func, args) = self.outputLists[0][count]
- fileobj.write(func(u'out',u'',u' ',args))
-
- fileobj.write(u' out.seek(0)\n')
- fileobj.write(u'\n\n return (out, handle)\n')
-
- def returnIf(self,bool,value):
- if bool:
- return value
- else:
- return ''
-
- def startDocument(self):
- print "Starting compile of %s" % self.name
- self.elementStack = []
- self.inInclude = False
- self.inRepeatView = False
- self.inRepeatWithTemplate = False
- self.inUpdateView = False
- self.inConfigUpdate = False
- self.inReplace = False
- self.inStaticReplace = False
- self.inExecOnUnload = False
- self.inExecOnLoad = False
- self.repeatDepth = 0
- self.hotspotDepth = -1
- self.hotspotTagName = ''
- self.replaceDepth = 0
- self.hiding = False
- self.hideDepth = []
- self.depth = 0
- self.bodyDepth = None
- self.repeatName = ''
- self.started = not self.onlyBody
- self.outputLists = [[]]
- self.outputText = []
- self.hidingParams = []
- self.translateDepth = []
- self.translateText = []
- self.translateDict = []
- self.translateName = []
- self.inRaw = False
- self.rawDepth = None
-
-
- def endDocument(self):
- print "Ending compile"
- self.endText()
-
- def startElement(self,name, attrs):
- self.depth = self.depth + 1
- if name == 'body':
- self.bodyDepth = self.depth
-
- if (len(self.translateDepth) > 0 and
- self.depth == self.translateDepth[-1]+1):
- if not attrs.has_key('i18n:name'):
- print "in raw for %s" % name
- self.inRaw = True
- self.rawDepth = self.depth
- else:
- self.translateName[-1] = attrs['i18n:name']
- self.translateText[-1] += u'${%s}' % self.translateName[-1]
- self.endText()
- self.outputLists.append([])
-
- if self.inRaw:
- self.translateText[-1] += u"<%s" % name
- for attr in attrs.keys():
- self.translateText[-1] += u' %s="%s"' % (attr, quoteattr(attrs[attr]))
- self.translateText[-1] += u'>'
- elif self.onlyBody and not self.started:
- if name == 'body':
- self.started = True
- elif not self.started:
- pass
- elif 't:repeatForView' in attrs.keys():
- if 't:containerDiv' in attrs.keys():
- self.inRepeatWithContainerDiv = True
- self.repeatContainerId = generateId()
- self.addElementStart('div', {'id': self.repeatContainerId})
- else:
- self.inRepeatWithContainerDiv = False
- self.startRepeat(attrs['t:repeatForView'])
- if not 't:repeatTemplate' in attrs.keys():
- self.addElementStart(name, attrs, addId=True)
- self.inRepeatWithTemplate = False
- else:
- name = attrs['t:repeatTemplate']
- self.addFillTemplate(name, addIdToTopLevelElement=True)
- # if t:repeatTemplate is set, there shouldn't be any children
- # of this element. Create a new outputList to check this.
- self.outputLists.append([])
- self.inRepeatWithTemplate = True
- elif 't:updateForView' in attrs.keys():
- self.startUpdate(attrs['t:updateForView'])
- self.addElementStart(name, attrs, addId=True)
- elif 't:updateForConfigChange' in attrs.keys():
- self.startConfigUpdate()
- self.addElementStart(name, attrs, addId=True)
- elif 't:hideIf' in attrs.keys():
- ifValue = attrs['t:hideIf']
- if attrs.has_key('t:updateHideOnView'):
- if self.inRepeatView or self.inUpdateView:
- print "Warning: t:updateHideOnView is unsupported inside a repeat view"
- self.addUpdateHideOnView(attrs['t:updateHideOnView'],name, ifValue, attrs)
- else:
- self.startHiding(ifValue)
- self.addElementStart(name, attrs)
- #FIXME: support i18n tags within a t:hideIf
- elif 't:showIf' in attrs.keys():
- ifValue = u"not (%s)" % attrs['t:showIf']
- if attrs.has_key('t:updateHideOnView'):
- if self.inRepeatView or self.inUpdateView:
- print "Warning: t:updateHideOnView is unsupported inside a repeat view"
- self.addUpdateHideOnView(attrs['t:updateHideOnView'],name, ifValue, attrs)
- else:
- self.startHiding(ifValue)
- self.addElementStart(name, attrs)
- #FIXME: support i18n tags within a t:showIf
- elif 'i18n:translate' in attrs.keys():
- self.addElementStart(name, attrs)
- self.translateDepth.append(self.depth)
- self.translateText.append('')
- self.translateDict.append({})
- self.translateName.append('')
-
- elif name == 't:include':
- self.addInclude(attrs['filename'])
-
- elif 't:replace' in attrs.keys():
- self.addElementStart(name, attrs)
- replace = attrs['t:replace']
- self.addEvalEscape(replace)
- self.inReplace = True
- self.replaceDepth = self.depth
- elif 't:replaceMarkup' in attrs.keys():
- self.addElementStart(name, attrs)
- replace = attrs['t:replaceMarkup']
- self.addEval(replace)
- self.inReplace = True
- self.replaceDepth = self.depth
- elif name == 't:execOnUnload':
- self.inExecOnUnload = True
- self.code = ''
- elif name == 't:execOnLoad':
- self.inExecOnLoad = True
- self.code = ''
- elif name == 't:include':
- f = open(resource.path('templates/%s'%attrs['filename']),'r')
- html = f.read()
- f.close()
- self.addText(html.decode('utf-8'))
- elif name == 't:staticReplaceMarkup':
- replace = attrs['t:replaceData']
- self.addEval(replace)
- self.inStaticReplace = True
- elif name == 't:staticReplace':
- replace = attrs['t:replaceData']
- self.addEvalEscape(replace)
- self.inStaticReplace = True
- elif name == 't:includeTemplate':
- self.addFillTemplate(attrs['filename'])
- elif name == 't:executeTemplate':
- args = dict(attrs.items())
- filename = args.pop('filename')
- self.addInstruction(genExecuteTemplate, (filename, args))
- elif name == 't:triggerActionOnLoad':
- self.handle.addTriggerActionURLOnLoad(attrs['url'])
- elif name == 't:triggerActionOnUnload':
- self.handle.addTriggerActionURLOnUnload(attrs['url'])
- elif 't:hotspot' in attrs.keys():
- self.addHotspot(name, attrs['t:hotspot'])
- else:
- self.addElementStart(name, attrs)
-
- def endElement(self,name):
- if not self.started:
- pass
- elif self.inRaw:
- self.translateText[-1] += u'</%s>' % name
- elif self.onlyBody and name == 'body':
- self.started = False
- elif name == 't:include':
- pass
- elif name == 't:staticReplace':
- self.inStaticReplace = False
- elif name == 't:staticReplaceMarkup':
- self.inStaticReplace = False
- elif name == 't:filter':
- pass
- elif name == 't:sort':
- pass
- elif name == 't:includeTemplate':
- pass
- elif name == 't:executeTemplate':
- pass
- elif name == 't:triggerActionOnUnload':
- pass
- elif name == 't:triggerActionOnLoad':
- pass
- elif self.hiding and self.depth == self.hideDepth[-1]:
- self.addText(u'</%s>'%name)
- self.endHiding()
- elif self.inReplace and self.depth == self.replaceDepth:
- self.addText(u'</%s>'%name)
- self.inReplace = False
- elif self.inRepeatView and self.depth == self.repeatDepth:
- if self.inRepeatWithTemplate:
- self.endText()
- nestedOutputs = self.outputLists.pop()
- if nestedOutputs:
- m = "Elements with t:repeatTemplate, can't have children"
- raise ValueError(m)
- else:
- self.addText(u'</%s>'%name)
- self.endText()
- repeatList = self.endRepeat()
- if self.inRepeatWithContainerDiv:
- self.endElement('div')
- self.handle.addView(self.repeatContainerId, 'containerDiv', repeatList, self.repeatName)
- else:
- repeatId = generateId()
- self.addText(u'<span id="%s"/>'%quoteattr(repeatId))
- self.handle.addView(repeatId, 'nextSibling', repeatList, self.repeatName)
- elif self.inUpdateView and self.depth == self.repeatDepth:
- self.addText(u'</%s>'%name)
- self.endText()
- repeatList = self.endUpdate()
- repeatId = generateId()
- self.addText(u'<span id="%s"/>'%quoteattr(repeatId))
- self.handle.addUpdate(repeatId, 'nextSibling', repeatList, self.repeatName)
- elif self.inConfigUpdate and self.depth == self.repeatDepth:
- self.addText(u'</%s>'%name)
- self.endText()
- repeatList = self.endConfigUpdate()
- repeatId = generateId()
- self.addText(u'<span id="%s"/>'%quoteattr(repeatId))
- self.handle.addConfigUpdate(repeatId, 'nextSibling', repeatList)
- elif (len(self.translateDepth) > 0 and
- self.depth == self.translateDepth[-1]):
- self.addTranslation()
- self.addText(u'</%s>'%name)
- elif self.depth == self.hotspotDepth:
- self.addText(u"</%s><!-- HOT SPOT END -->" % self.hotspotTagName)
- self.hotspotDepth = -1
- elif name == 't:execOnUnload':
- self.inExecOnUnload = False
- self.handle.addExecOnUnload(self.code)
- elif name == 't:execOnLoad':
- self.inExecOnLoad = False
- self.handle.addExecOnLoad(self.code)
- else:
- self.addText(u'</%s>'%name)
- if (len(self.translateDepth) > 0 and
- self.depth == self.translateDepth[-1]+1):
- if self.inRaw:
- print "ending raw for %s" % name
- self.inRaw = False
- else:
- self.endText()
- self.translateDict[-1][self.translateName[-1]] = self.outputLists.pop()
- self.depth = self.depth - 1
-
- def characters(self,data):
- if not self.started:
- pass
- elif self.inReplace or self.inStaticReplace:
- pass
- elif ((len(self.translateDepth) > 0 and
- self.depth == self.translateDepth[-1]) or
- self.inRaw):
- self.translateText[-1] += data
- elif self.inExecOnUnload or self.inExecOnLoad:
- self.code += data
- else:
- self.addTextEscape(data)
-
- def skippedEntity(self, name):
- self.addText(u"&%s;" % name)
-
- def addTranslation(self):
- self.translateDepth.pop()
- self.translateName.pop()
- self.addInstruction(genRepeatTranslate, (self.translateText.pop(), self.translateDict.pop()))
-
- def addInclude(self, template):
- f = open(resource.path('templates/%s'%template),'r')
- html = f.read()
- f.close()
- self.addText(html)
-
- def addFillTemplate(self, name, *args, **kwargs):
- print " compiling '%s' subtemplate" % name
- (tcc, handle) = compileTemplate(name, onlyBody=True, *args, **kwargs)
- if tcc.isDynamic:
- self.isDynamic = True
- if self.inUpdateView or self.inConfigUpdate or self.inRepeatView:
- raise TemplateError, "Nested Dynamic tags"
- self.handle.addSubHandle(handle)
- self.addInstructions(tcc.getOperationList())
-
- def addIdAndClose(self):
- self.addText(' id="')
- self.addInstruction(genRepeatTID,None)
- self.addText('">')
-
- def startRepeat(self, name):
- if self.inUpdateView or self.inConfigUpdate or self.inRepeatView:
- raise TemplateError, "Nested Dynamic tags"
- self.endText()
- self.inRepeatView = True
- self.isDynamic = True
- self.repeatDepth = self.depth
- self.repeatName = name
- self.outputLists.append([])
-
- def startUpdate(self, name):
- if self.inUpdateView or self.inConfigUpdate or self.inRepeatView:
- raise TemplateError, "Nested Dynamic tags"
- self.endText()
- self.inUpdateView = True
- self.isDynamic = True
- self.repeatDepth = self.depth
- self.repeatName = name
- self.outputLists.append([])
-
- def startConfigUpdate(self):
- if self.inUpdateView or self.inConfigUpdate or self.inRepeatView:
- raise TemplateError, "Nested Dynamic tags"
- self.endText()
- self.inConfigUpdate = True
- self.isDynamic = True
- self.repeatDepth = self.depth
- self.outputLists.append([])
-
- def endRepeat(self):
- self.inRepeatView = False
- return self.outputLists.pop()
-
- def endUpdate(self):
- self.inUpdateView = False
- return self.outputLists.pop()
-
- def endConfigUpdate(self):
- self.inConfigUpdate = False
- return self.outputLists.pop()
-
- def startHiding(self,ifValue):
- self.endText()
- self.hidingParams.append(ifValue)
- self.outputLists.append([])
- self.hideDepth.append(self.depth)
- self.hiding = True
-
- def endHiding(self):
- self.endText()
- ifValue = self.hidingParams.pop()
- funcList = self.outputLists.pop()
- self.hideDepth.pop()
- self.hiding = len(self.hideDepth) > 0
- self.addInstruction(genHideSection, (ifValue, funcList))
-
- def addText(self, text):
- self.outputText.append( text)
-
- def addElementStart(self, name, attrs, addId=False):
- if (self.bodyDepth is not None and self.depth == self.bodyDepth + 1
- and self.addIdToTopLevelElement):
- addId = True
- self.addText(u'<%s'%name)
- for key in attrs.keys():
- if (not (key.startswith('t:') or key.startswith('i18n:')) or
- key == 't:contextMenu'):
- self.addAttr(key,attrs[key])
- if name.lower() == 'body':
- self.addInstruction(genInsertBodyTagExtra, None)
- if addId:
- self.addIdAndClose()
- else:
- self.addText(u'>')
-
- def addTextEscape(self, text):
- self.outputText.append( escape(text))
-
- def addUpdateHideOnView(self, viewName, name, ifValue, attrs):
- nodeId = generateId()
- self.addText(u"<%s" % name)
- for key in attrs.keys():
- if not key in ['t:hideIf','t:updateHideOnView','style']:
- self.addText(u" %s=" % key)
- self.addInstruction(genQuoteAndFillAttr, attrs[key])
- self.addText(u' id="%s"' % quoteattr(nodeId))
- self.addInstruction(genUpdateHideOnView,(viewName, ifValue, attrs, nodeId))
-
- def addAttr(self, attr, value):
- self.addText(u' %s="' % attr)
- self.addDynamicText(value)
- self.addText('"')
-
- def addDynamicText(self, value):
- match = attrPattern.match(value)
- if match:
- while match:
- self.addText(quoteattr(match.group(1)))
- self.addInstruction(genQuoteAttr,match.group(2))
- value = match.group(3)
- match = attrPattern.match(value)
- self.addText(u'%s' % quoteattr(value))
- else:
- match = rawAttrPattern.match(value)
- if match:
- while match:
- self.addText(quoteattr(match.group(1)))
- self.addInstruction(genRawAttr,match.group(2))
- value = match.group(3)
- match = rawAttrPattern.match(value)
- self.addText(u'%s' % quoteattr(value))
- else:
- match = resourcePattern.match(value)
- if match:
- self.addInstruction(genRawAttr,u'resources.url(%s)'%repr(match.group(1)))
- else:
- self.addText(quoteattr(value))
-
- def addHotspot(self, name, id):
- self.hotspotDepth = self.depth
- self.hotspotTagName = name
- self.addText(u"<!-- HOT SPOT ")
- self.addDynamicText(id)
- self.addText(u" -->")
- self.addElementStart(name, {'id': id})
-
- def addEval(self,replace):
- self.addInstruction(genRepeatEval,replace)
-
- def addEvalEscape(self,replace):
- self.addInstruction(genRepeatEvalEscape, replace)
-
- def endText(self):
- if len(self.outputText) > 0:
- self.addInstruction(genRepeatText,u''.join(self.outputText))
- self.outputText = []
-
- def addInstruction(self, instruction, args):
- if instruction != genRepeatText:
- self.endText()
- self.outputLists[-1].append((instruction,args))
-
- def addInstructions(self, instructions):
- self.endText()
- for (ins, arg) in instructions:
- self.addInstruction(ins, arg)
-
- ###############################################################################
- #### Generating Javascript callbacks to keep document updated ####
- ###############################################################################
-
- # Object representing data needed to register Javascript callbacks
- #
- # This is used by TemplateContentCompiler to generate code to create a
- # Handle
- class MetaHandle:
- def __init__(self):
- self.trackedViews = []
- self.updateRegions = []
- self.configUpdateRegions = []
- self.subHandles = []
- self.triggerActionURLsOnLoad = []
- self.triggerActionURLsOnUnload = []
- self.execOnUnload = None
- self.execOnLoad = None
-
- def addTriggerActionURLOnLoad(self,url):
- self.triggerActionURLsOnLoad.append(str(url))
-
- def addTriggerActionURLOnUnload(self, url):
- self.triggerActionURLsOnUnload.append(str(url))
-
- def getTriggerActionURLsOnLoad(self):
- return self.triggerActionURLsOnLoad
-
- def getTriggerActionURLsOnUnload(self):
- return self.triggerActionURLsOnUnload
-
- def addExecOnUnload(self, code):
- self.execOnUnload = code
-
- def addExecOnLoad(self, code):
- self.execOnLoad = code
-
- def addView(self, anchorId, anchorType, templateFuncs, name):
- # Register for JS calls to populate a t:repeatFor. 'view' is the
- # database view to track; 'node' is a DOM node representing the
- # template to fill; 'data' are extra variables to be used in expanding
- # the template. The 'anchor*' arguments tell where in the document
- # to place the expanded template nodes. If 'anchorType' is
- # 'nextSibling', 'anchorId' is the id attribute of the tag immediately
- # following the place the template should be expanded. If it is
- # 'parentNode', the template should be expanded so as to be the final
- # child in the node whose id attribute matches 'anchorId'.
- # 'containerDiv' is like parentNode, except it's contained in an
- # auto-generated <div> element. This allows for efficient changes
- # when the view is re-sorted.
- #
- # We take a private copy of 'node', so don't worry about modifying
- # it subsequent to calling this method.
- tv = (anchorId, anchorType, templateFuncs, name)
- self.trackedViews.append(tv)
-
- def addUpdate(self, anchorId, anchorType, templateFuncs, name):
- ur = (anchorId, anchorType, templateFuncs, name)
- self.updateRegions.append(ur)
-
- def addConfigUpdate(self, anchorId, anchorType, templateFuncs):
- ur = (anchorId, anchorType, templateFuncs)
- self.configUpdateRegions.append(ur)
-
- def addSubHandle(self, handle):
- self.subHandles.append(handle)
-
- def render(self, fileobj, varname = 'handle'):
- prefix = u' '
- ending = u"\n"
-
- fileobj.write(u'%s# Start of handle%s%s' % (prefix, ending, ending))
-
- fileobj.write(u'%s# Start user code%s' % (prefix, ending))
- if self.execOnLoad is not None:
- for line in self.execOnLoad.splitlines():
- fileobj.write(u'%s%s%s' % (prefix, line, ending))
- if self.execOnUnload is not None:
- fileobj.write(u'%s%sdef _execOnUnload():%s' % (ending, prefix, ending))
- for line in self.execOnUnload.splitlines():
- fileobj.write(u'%s %s%s' % (prefix, line, ending))
- fileobj.write(u'%s# End user code%s%s' % (prefix, ending, ending))
-
- fileobj.write(u'%slocalvars = locals()%s' % (prefix, ending))
- fileobj.write(u'%slocalvars.update(globals())%s' % (prefix, ending))
- if self.execOnUnload is not None:
- fileobj.write(u'%s%s = Handle(domHandler, localvars, onUnlink = _execOnUnload)%s%s' % (prefix, varname, ending, ending))
- else:
- fileobj.write(u'%s%s = Handle(domHandler, localvars, onUnlink = lambda:None)%s%s' % (prefix, varname, ending, ending))
-
- count = 0
- for ur in self.updateRegions:
- (anchorId, anchorType, templateFuncs, name) = ur
- upFunc = u"up_%s_%s" % (count, varname)
- fileobj.write(u'%sdef %s(viewName, view, tid):%s' % (prefix, upFunc,ending))
- fileobj.write(u'%s out = StringIO()%s' % (prefix, ending))
- for count2 in range(len(templateFuncs)):
- (func, args) = templateFuncs[count2]
- fileobj.write(func(u'out',u'',prefix+u' ',args))
- fileobj.write(u'%s out.seek(0)%s' % (prefix, ending))
- fileobj.write(u'%s return out%s' % (prefix, ending))
-
- fileobj.write(u'%s%s.addUpdate(%s,%s,%s,%s, %s)%s' % (prefix, varname, repr(anchorId),repr(anchorType),name,upFunc,repr(name),ending))
- count += 1
-
- for ur in self.configUpdateRegions:
- (anchorId, anchorType, templateFuncs) = ur
- upFunc = u"config_up_%s_%s" % (count, varname)
- fileobj.write(u'%sdef %s(tid):%s' % (prefix, upFunc, ending))
- fileobj.write(u'%s out = StringIO()%s' % (prefix, ending))
- for count2 in range(len(templateFuncs)):
- (func, args) = templateFuncs[count2]
- fileobj.write(func('out','',prefix+' ',args))
- fileobj.write(u'%s out.seek(0)%s' % (prefix, ending))
- fileobj.write(u'%s return out%s' % (prefix, ending))
-
- fileobj.write(u'%s%s.addConfigUpdate(%s,%s,%s)%s' % (prefix, varname, repr(anchorId),repr(anchorType),upFunc,ending))
- count += 1
-
- for tv in self.trackedViews:
- (anchorId, anchorType, templateFuncs, name) = tv
- repFunc = u"rep_%s_%s" % (count, varname)
- fileobj.write(u'%sdef %s(this, viewName, view, tid):%s' % (prefix, repFunc,ending))
- fileobj.write(u'%s out = StringIO()%s' % (prefix, ending))
- for count2 in range(len(templateFuncs)):
- (func, args) = templateFuncs[count2]
- fileobj.write(func('out','',prefix+' ',args))
- fileobj.write(u'%s out.seek(0)%s' % (prefix, ending))
- fileobj.write(u'%s return out%s' % (prefix, ending))
-
- fileobj.write(u'%s%s.addView(%s,%s,%s,%s, %s)%s' % (prefix, varname, repr(anchorId),repr(anchorType),name,repFunc,repr(name),ending))
- count += 1
-
- for action in self.triggerActionURLsOnLoad:
- fileobj.write(u'%s%s.addTriggerActionURLOnLoad(fillAttr(%s,locals()))%s' %
- (prefix, varname, repr(action), ending))
-
- for action in self.triggerActionURLsOnUnload:
- fileobj.write(u'%s%s.addTriggerActionURLOnUnload(fillAttr(%s,locals()))%s' %
- (prefix, varname, repr(action), ending))
-
- for subHandle in range(len(self.subHandles)):
- newVarName = u'%s_%d' % (varname, subHandle)
- self.subHandles[subHandle].render(fileobj, newVarName)
- fileobj.write(u'%s%s.addSubHandle(%s)%s'%
- (prefix,varname,newVarName,ending))
-
-
- def fillIfNotNone(obj):
- if obj is None:
- return repr(None)
- else:
- return u"fillAttr(%s,locals())" % repr(obj)
-